/*
 * ImageIO.cpp
 *
 *  Created on: Aug 27, 2011
 *      Author: ryan
 */

#include "ImageIO.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

unsigned char ImageIO::Bmp_headbuf[HEADERSIZE];
unsigned char ImageIO::Bmp_Pallet[PALLETSIZE];
char ImageIO::Bmp_type[2];
unsigned long ImageIO::Bmp_size;
unsigned int ImageIO::Bmp_info_header_size;
unsigned int ImageIO::Bmp_header_size;
int ImageIO::Bmp_height;
int ImageIO::Bmp_width;
unsigned short ImageIO::Bmp_planes;
unsigned short ImageIO::Bmp_color;
long ImageIO::Bmp_comp;
long ImageIO::Bmp_image_size;
long ImageIO::Bmp_xppm;
long ImageIO::Bmp_yppm;

void ImageIO::ReadBmpImage(char *filename, Image *Imagep) {
	int i, j;
	int Real_width;
	FILE *Bmp_Fp = fopen(filename, "rb");
	unsigned char *Bmp_Data;

	if (Bmp_Fp == NULL) {
		fprintf(stderr, "Error: file %s couldn\'t open for read!.\n", filename);
		exit(1);
	}

	fread(Bmp_headbuf, sizeof(unsigned char), HEADERSIZE, Bmp_Fp);

	memcpy(&Bmp_type, Bmp_headbuf, sizeof(Bmp_type));
	if (strncmp(Bmp_type, "BM", 2) != 0) {
		fprintf(stderr, "Error: %s is not a bmp file.\n", filename);
		exit(1);
	}

	memcpy(&Imagep->width, Bmp_headbuf + 18, sizeof(Bmp_width));
	memcpy(&Imagep->height, Bmp_headbuf + 22, sizeof(Bmp_height));
	memcpy(&Bmp_color, Bmp_headbuf + 28, sizeof(Bmp_color));
	if (Bmp_color != 24) {
		fprintf(stderr,
				"Error: Bmp_Color = %d is not implemented in this program.\n",
				Bmp_color);
		exit(1);
	}

	Real_width = Imagep->width * 3 + Imagep->width % 4;

	if ((Bmp_Data = (unsigned char *) calloc(Real_width, sizeof(unsigned char)))
			== NULL) {
		fprintf(stderr, "Error: Memory allocation failed for Bmp_Data!\n");
		exit(1);
	}

	Imagep->data = new Color*[Imagep->width];
	for (i = 0; i < Imagep->width; ++i) {
		Imagep->data[i] = new Color[Imagep->height];
	}
	for (i = 0; i < Imagep->height; i++) {
		fread(Bmp_Data, 1, Real_width, Bmp_Fp);
		int cur = 0;
		for (j = 0; j < Imagep->width; j++) {
			Color *c = &Imagep->data[j][Imagep->height - i - 1];
			c->b = Bmp_Data[cur++];
			c->g = Bmp_Data[cur++];
			c->r = Bmp_Data[cur++];
			if (c->b != 255 || c->g != 0 || c->r != 0) {
				int v = max_arg3(c->b, c->g, c->r);
				c->r = c->g = c->b = v;
			}
		}
	}
	free(Bmp_Data);
	fclose(Bmp_Fp);
}

void ImageIO::WriteBmpImage(char *filename, Image *tp) {
	int i, j;
	int Real_width;
	FILE *Out_Fp = fopen(filename, "wb");
	unsigned char *Bmp_Data;

	if (Out_Fp == NULL) {
		fprintf(stderr, "Error: file %s couldn\'t open for write!\n", filename);
		exit(1);
	}

	Bmp_color = 24;
	Bmp_header_size = HEADERSIZE;
	Bmp_info_header_size = 40;
	Bmp_planes = 1;

	Real_width = tp->width * 3 + tp->width % 4;

	if ((Bmp_Data = (unsigned char *) calloc(Real_width, sizeof(unsigned char)))
			== NULL) {
		fprintf(stderr, "Error: Memory allocation failed for Bmp_Data!\n");
		exit(1);
	}

	Bmp_xppm = Bmp_yppm = 0;
	Bmp_image_size = tp->height * Real_width;
	Bmp_size = Bmp_image_size + HEADERSIZE;
	Bmp_headbuf[0] = 'B';
	Bmp_headbuf[1] = 'M';
	memcpy(Bmp_headbuf + 2, &Bmp_size, sizeof(Bmp_size));
	Bmp_headbuf[6] = Bmp_headbuf[7] = Bmp_headbuf[8] = Bmp_headbuf[9] = 0;
	memcpy(Bmp_headbuf + 10, &Bmp_header_size, sizeof(Bmp_header_size));
	Bmp_headbuf[11] = Bmp_headbuf[12] = Bmp_headbuf[13] = 0;
	memcpy(Bmp_headbuf + 14, &Bmp_info_header_size,
			sizeof(Bmp_info_header_size));
	Bmp_headbuf[15] = Bmp_headbuf[16] = Bmp_headbuf[17] = 0;
	memcpy(Bmp_headbuf + 18, &tp->width, sizeof(Bmp_width));
	memcpy(Bmp_headbuf + 22, &tp->height, sizeof(Bmp_height));
	memcpy(Bmp_headbuf + 26, &Bmp_planes, sizeof(Bmp_planes));
	memcpy(Bmp_headbuf + 28, &Bmp_color, sizeof(Bmp_color));
	memcpy(Bmp_headbuf + 34, &Bmp_image_size, sizeof(Bmp_image_size));
	memcpy(Bmp_headbuf + 38, &Bmp_xppm, sizeof(Bmp_xppm));
	memcpy(Bmp_headbuf + 42, &Bmp_yppm, sizeof(Bmp_yppm));
	Bmp_headbuf[46] = Bmp_headbuf[47] = Bmp_headbuf[48] = Bmp_headbuf[49] = 0;
	Bmp_headbuf[50] = Bmp_headbuf[51] = Bmp_headbuf[52] = Bmp_headbuf[53] = 0;

	fwrite(Bmp_headbuf, sizeof(unsigned char), HEADERSIZE, Out_Fp);
	for (i = 0; i < tp->height; i++) {
		int cur = 0;
		for (j = 0; j < tp->width; j++) {
			Color *c = &tp->data[j][tp->height - i - 1];
			Bmp_Data[cur++] = c->b;
			Bmp_Data[cur++] = c->g;
			Bmp_Data[cur++] = c->r;
		}
		for (j = tp->width * 3; j < Real_width; j++) {
			Bmp_Data[j] = 0;
		}
		fwrite(Bmp_Data, sizeof(unsigned char), Real_width, Out_Fp);
	}

	free(Bmp_Data);
	fclose(Out_Fp);
}

void ImageIO::PrintBmpInfo(char *filename) {
	FILE *Bmp_Fp = fopen(filename, "rb");
	if (Bmp_Fp == NULL) {
		fprintf(stderr, "Error: file %s couldn\'t open for write!\n", filename);
		exit(1);
	}

	fread(Bmp_headbuf, sizeof(unsigned char), HEADERSIZE, Bmp_Fp);

	memcpy(&Bmp_type, Bmp_headbuf, sizeof(Bmp_type));
	if (strncmp(Bmp_type, "BM", 2) != 0) {
		fprintf(stderr, "Error: %s is not a bmp file.\n", filename);
		exit(1);
	}
	memcpy(&Bmp_size, Bmp_headbuf + 2, sizeof(Bmp_size));
	memcpy(&Bmp_width, Bmp_headbuf + 18, sizeof(Bmp_width));
	memcpy(&Bmp_height, Bmp_headbuf + 22, sizeof(Bmp_height));
	memcpy(&Bmp_color, Bmp_headbuf + 28, sizeof(Bmp_color));
	memcpy(&Bmp_comp, Bmp_headbuf + 30, sizeof(Bmp_comp));
	memcpy(&Bmp_image_size, Bmp_headbuf + 34, sizeof(Bmp_size));
	memcpy(&Bmp_xppm, Bmp_headbuf + 38, sizeof(Bmp_xppm));
	memcpy(&Bmp_yppm, Bmp_headbuf + 42, sizeof(Bmp_yppm));

	printf("�t�@�C����       = %s \n", filename);
	printf("�t�@�C���^�C�v   = %c%c \n", Bmp_type[0], Bmp_type[1]);
	printf("�t�@�C���T�C�Y   = %ld (byte)\n", Bmp_size);
	printf("��               = %d (pixel)\n", Bmp_width);
	printf("����             = %d (pixel)\n", Bmp_height);
	printf("�F               = %d (bit)\n", Bmp_color);
	printf("���k             = %ld\n", Bmp_comp);
	printf("�摜�����̃T�C�Y = %ld (byte)\n", Bmp_image_size);
	printf("�����𑜓x       = %ld (ppm)\n", Bmp_xppm);
	printf("�����𑜓x       = %ld (ppm)\n", Bmp_yppm);

	fclose(Bmp_Fp);
}

void ImageIO::HMirror(Image *sp, Image *tp) {
	int i, j;
	for (i = 0; i < tp->height; i++)
		for (j = 0; j < tp->width; j++)
			sp->data[j][tp->height - i - 1] = tp->data[j][i];
	sp->height = tp->height;
	sp->width = tp->width;
}

void ImageIO::VMirror(Image *sp, Image *tp) {
	int i, j;
	for (i = 0; i < tp->height; i++)
		for (j = 0; j < tp->width; j++)
			sp->data[tp->width - j - 1][i] = tp->data[j][i];
	sp->height = tp->height;
	sp->width = tp->width;
}

void ImageIO::Rotate90(int a, Image *sp, Image *tp) {
	int i, j;
	if ((a % 4) == 0) {
		for (i = 0; i < tp->height; i++)
			for (j = 0; j < tp->width; j++)
				sp->data[j][i] = tp->data[j][i];
		sp->width = tp->width;
		sp->height = tp->height;
	} else if ((a % 4) == 1) {
		for (i = 0; i < tp->height; i++)
			for (j = 0; j < tp->width; j++)
				sp->data[tp->height - i - 1][j] = tp->data[j][i];
		sp->height = tp->width;
		sp->width = tp->height;
	} else if ((a % 4) == 2) {
		for (i = 0; i < tp->height; i++)
			for (j = 0; j < tp->width; j++)
				sp->data[tp->width - j - 1][tp->height - i - 1] =
						tp->data[j][i];
		sp->width = tp->width;
		sp->height = tp->height;
	} else {
		for (i = 0; i < tp->height; i++)
			for (j = 0; j < tp->width; j++)
				sp->data[i][tp->width - j - 1] = tp->data[j][i];
		sp->height = tp->width;
		sp->width = tp->height;
	}
}

void ImageIO::Shrink(int a, Image *sp, Image *tp) {
	int i, j, k, l, w, h, count;
	unsigned long tmp_r, tmp_g, tmp_b;

	sp->height = tp->height / a + ((tp->height % a == 0) ? 0 : 1);sp
	->width = tp->width / a + ((tp->width % a == 0) ? 0 : 1);

	for
(	i = 0; i < sp->height; i++) {
		h = (a * (i + 1) <= tp->height) ? a : tp->height - a * i;
		for (j = 0; j < sp->width; j++) {
			w = (a * (j + 1) <= tp->width) ? a : tp->width - a * j;
			tmp_r = 0;
			tmp_g = 0;
			tmp_b = 0;
			count = 0;
			for (k = 0; k < h; k++)
			for (l = 0; l < w; l++) {
				Color *c = &tp->data[j * a + l][i * a + k];
				tmp_r += c->r;
				tmp_g += c->g;
				tmp_b += c->b;
				count++;
			}
			sp->data[j][i].r = (unsigned char) (tmp_r / count);
			sp->data[j][i].g = (unsigned char) (tmp_g / count);
			sp->data[j][i].b = (unsigned char) (tmp_b / count);
		}
	}
}

void ImageIO::Mosaic(int a, Image *sp, Image *tp) {
	int i, j, k, l, w, h, t_height, t_width, count;
	unsigned long tmp_r, tmp_g, tmp_b;

	sp->height = tp->height;
	sp->width = tp->width;

	t_height = tp->height / a + ((tp->height % a == 0) ? 0 : 1);t_width
	= tp->width / a + ((tp->width % a == 0) ? 0 : 1);

	for
(	i = 0; i < t_height; i++) {
		h = (a * (i + 1) <= tp->height) ? a : tp->height - a * i;
		for (j = 0; j < t_width; j++) {
			w = (a * (j + 1) <= tp->width) ? a : tp->width - a * j;
			tmp_r = 0;
			tmp_g = 0;
			tmp_b = 0;
			count = 0;
			for (k = 0; k < h; k++)
			for (l = 0; l < w; l++) {
				Color *c = &tp->data[j * a + l][i * a + k];
				tmp_r += c->r;
				tmp_g += c->g;
				tmp_b += c->b;
				count++;
			}
			tmp_r = (unsigned char) (tmp_r / count);
			tmp_g = (unsigned char) (tmp_g / count);
			tmp_b = (unsigned char) (tmp_b / count);
			for (k = 0; k < h; k++)
			for (l = 0; l < w; l++) {
				Color *c = &sp->data[j * a + l][i * a + k];
				c->r = tmp_r;
				c->g = tmp_g;
				c->b = tmp_b;
			}
		}
	}
}

void ImageIO::Gray(Image *sp, Image *tp) {
	int i, j;
	unsigned char tmp;
	sp->height = tp->height;
	sp->width = tp->width;

	for (i = 0; i < sp->height; i++) {
		for (j = 0; j < sp->width; j++) {
			tmp = (unsigned char) ((tp->data[j][i].r + tp->data[j][i].g
					+ tp->data[j][i].b) / 3);
			sp->data[j][i].r = sp->data[j][i].g = sp->data[j][i].b = tmp;
		}
	}
}

void ImageIO::Diminish(Image *sp, Image *tp, unsigned char x) {
	int i, j;
	unsigned char y;

	x = x % 8;
	y = 255 << x;

	sp->height = tp->height;
	sp->width = tp->width;

	for (i = 0; i < sp->height; i++) {
		for (j = 0; j < sp->width; j++) {
			sp->data[j][i].r = tp->data[j][i].r & y;
			sp->data[j][i].g = tp->data[j][i].g & y;
			sp->data[j][i].b = tp->data[j][i].b & y;
		}
	}
}
